const path = require('path');
const types = require('babel-types');
const generate = require('babel-generator').default;
const traverse = require('babel-traverse').default;
const async = require('neo-async');

class NormalModule{
    constructor({name,context,rawRequest,resource,parser}) {

        this.name = name;
        this.context = context;// /Users/adminx/Desktop/Ikki-study/webpack-advanced/webpack5-base/16.hand/
        this.rawRequest = rawRequest;//src/index.js
        
        this.resource = resource;//这是这个模块的绝对路径 /Users/adminx/Desktop/Ikki-study/webpack-advanced/webpack5-base/16.hand/src/index.js
        this.parser = parser; //这是AST解析器,可以把源代码转成AST抽象语法树
        this._source; //此模块对应的源代码
        this._ast; //此模块对应的AST抽象语法树
        this.dependencies = []; //当前模块依赖的模块信息
        this.blocks = []; //当前模块依赖哪些异步模块 import(那些模块)
        this.async=async;  //表示当前的模块是属于一个异步代码块,还是一个同步代码块
    }
    /**
     * 编译本模块
     * @param {*} compilation 
     * @param {*} callback 
    */
    build(compilation,callback){
        this.doBuild(compilation,err=>{
            //得到语法树
            this._ast = this.parser.parse(this._source);
            //遍历语法树,找到里面的依赖进行收集依赖
            traverse(this._ast,{
                //当遍历到CallExpression节点的时候,就会进入回调
                CallExpression:(nodePath)=>{
                    let node = nodePath.node;//获取节点
                    if(node.callee.name === 'require'){//如果方法名是require方法的话
                        //1.把方法名用require改成了__webpack_require__
                        node.callee.name = '__webpack_require__';
                        let moduleName = node.arguments[0].value;//1.模块的名称
                        //依赖的绝对路径
                        let depResource;
                        //如果说模块的名字是以.开头,说明是一个本地模块,或者说用户自定义模块
                        if(moduleName.startsWith('.')){
                          //2.获得可能的扩展名
                          let extName = moduleName.split(path.posix.sep).pop().indexOf('.')==-1?'.js':'';
                          //3.获取依赖模块(./src/title.js)的绝对路径 win \ linux /
                          //C:\aproject\zhufeng202009webpack\8.my\src\title.js
                          depResource = path.posix.join(path.posix.dirname(this.resource),moduleName+extName);
                        }else{//否则是一个第三方模块,也就是放在node_modules里的
                          //C:\aproject\zhufeng202009webpack\8.my\node_modules\isarray\index.js
                          depResource = require.resolve(path.posix.join(this.context,'node_modules',moduleName))
                          depResource=depResource.replace(/\\/g,'/');//把window里的 \转成 /
                        }
                    }
                }
            })

            // callback()
        })
    }
    /**
     * 1.读取模块的源代码
     * @param {*} compilation 
     * @param {*} callback 
    */
    doBuild(compilation,callback){
        this.getSource(compilation,(err,source)=>{
            // 把最原始的代码存放到当前的模块_source上
            this._source = source
            callback()
        })
    }
    /**
     * 读取真正的源代码
     */
    getSource(compilation,callback){
        //fs.readFile读文件
        compilation.inputFileSystem.readFile(this.resource,'utf8',callback);
    }
}
module.exports = NormalModule
/**
 * 1.从硬盘上把模块内容读出来,读成一个文本
 * 2.可能它不是一个JS模块,所以会可能要走loader的转换,最终肯定要得到一个JS模块代码,得不到就报错了
 * 3.把这个JS模块代码经过parser处理转成抽象语法树AST
 * 4.分析AST里面的依赖,也就是找 require import节点,分析依赖的模块
 * 5.递归的编译依赖的模块
 * 6.不停的依次递归执行上面5步,直到所有的模块都编译完成为止 
 */